package zookeeper

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/go-zookeeper/zk"
	"log"
	"strings"
	"time"
)

type Register struct {
	conn     *zk.Conn
	stop     chan bool
	interval time.Duration // 心跳检测间隔
	node     *NodeInfo
}

func NewRegister(svrName string, conn *zk.Conn, ipAddr string, port int) *Register {
	return &Register{
		conn:     conn,
		interval: 3 * time.Second,
		stop:     make(chan bool, 1),
		node: &NodeInfo{
			SvrName: svrName,
			IpAddr:  ipAddr,
			Port:    port,
		},
	}
}

func (r *Register) Reg() error {
	err := r.doReg()
	if err != nil {
		log.Println("注册失败，退出")
		return err
	}
	go func() {
		t := time.NewTicker(r.interval)
		for {
			select {
			case ttl := <-t.C:
				key := getRegKey(r.node.SvrName, r.node.SvrID)
				exists, _, err := r.conn.Exists(key)
				//log.Printf("node:%+v, key:%v exist:%v\n", r.node, key, exists)
				if err != nil {
					log.Printf("heartbeat check for zk err: k:%v t:[%v]\n", r.node.SvrName, ttl)
					continue
				}
				if !exists {
					r.doReg()
				}
				//log.Printf("heartbeat check for zk k:%v t:[%v]\n", key, ttl)
			case <-r.stop:
				log.Println("程序退出，心跳检测协程结束")
				return
			}
		}
	}()
	return nil
}

func (r *Register) doReg() error {
	node := r.node
	rSvrName := getRegKeyPrefix(node.SvrName)
	nby, err := json.Marshal(node)
	if err != nil {
		log.Printf("node:%v to json string err:%v\n", node, err)
		return err
	}
	gKey, err := createSvr(rSvrName, string(nby), r.conn)
	if err != nil {
		log.Printf("zk create svr error %v\n", err)
		return err
	}
	// /services/s3/_c_2c0022ff8f5ff8339e1851095b0309f3-0000000002
	gKeys := strings.Split(gKey, "/")
	if len(gKeys) != 4 {
		return errors.New(fmt.Sprintf("invalid zk gen key:%v", gKey))
	}
	r.node.SvrID = gKeys[3]

	return nil
}

func createSvr(svrName string, data string, conn *zk.Conn) (string, error) {
	log.Println("svrName:", svrName)
	sl := strings.Split(svrName, "/")
	if len(sl) != 3 {
		return "", errors.New(fmt.Sprintf("invalid service key want want /services/sv1/ actor:%v", svrName))
	}
	err := ensureNode(fmt.Sprintf("/%s", sl[1]), "", conn)
	if err != nil {
		return "", err
	}
	log.Println("root")
	err = ensureNode(fmt.Sprintf("/%s/%s", sl[1], sl[2]), "", conn)
	if err != nil {
		return "", err
	}
	log.Println("svr")
	gKey, err := conn.CreateProtectedEphemeralSequential(fmt.Sprintf("%s/", svrName), []byte(data), zk.WorldACL(zk.PermAll))
	if err != nil {
		log.Printf("CreateProtectedEphemeralSequential err:%v", err)
		return "", err
	}
	log.Println("node")
	return gKey, nil
}

func ensureNode(svrName string, data string, conn *zk.Conn) error {
	exist, _, werr := conn.Exists(svrName)
	if !exist {
		if werr != nil {
			log.Fatalf("zk node:%v get err %v", svrName, werr)
			return werr
		}
		_, cerr := conn.CreateContainer(svrName, []byte(data), zk.FlagTTL, zk.WorldACL(zk.PermAll))
		if cerr != nil {
			log.Fatalf("zk svrName:%+v set err %v", svrName, cerr)
			return cerr
		}
	}
	return nil
}

func (r *Register) UnReg() {
	r.stop <- true
	r.stop = make(chan bool, 1)
	key := getRegKey(r.node.SvrName, r.node.SvrID)
	_, stat, err := r.conn.Get(key)
	if err != nil {
		log.Fatalf("zk node:%v get err %v", r.node, err)
	}
	if err = r.conn.Delete(key, stat.Version); err != nil {
		log.Fatalln(err)
	}
	log.Printf("服务：%v 摘除成功\n", r.node.SvrName)
}
